import React from 'react'
import { createContext } from 'react'
/*
    hooks ： 函数式组件使用的钩子， 只能在函数式组件中使用
    函数组件更匹配react设计理念 UI=f(data)
    Hooks是为了让函数组件可以有自己的状态，之前的函数组件不可以有自己的状态的
*/
// useState
import {useState, useEffect, useRef, useContext} from 'react'     // 1、引入 useState 函数

function Test() {
    // 2、执行useState函数传入初始值， 3、并解构 [数据，修改数据的方法]
    const [count, setCount] = useState(0)     
    /*
        useState传过来的参数 作为count 的初始值
        useState返回值是一个数组， 所以解构为一个数组，名字可以随便定义，但解构顺序不能变
        setCount函数，用来修改count, 但不能直接修改原值，会生成一个新值替换
        解构出来的是一对，是绑在一起的，setXXX 函数只能用来修改对应的 XXX 的值
    */
   /*
        当调用setCount的时候，更新过程
            首次渲染：首次渲染时，组件内部代码会被执行一次，其中useState会跟着执行， 传入的初始值参数只在首次渲染时生效

            更新渲染：setCount都会更新
                组件会再次渲染，函数组件这个函数会再次执行
                useState也会再次执行，得到会是新值，修改之后的值， 模板会用新值渲染
    */
    // useState 可以使用多次 , 每一次都是各自独立的
    // const [flag, setFlag] = useState(true)
    // const [list, setList] = useState([])
    // useState只能写在函数组件中， 只能写在最外层，不能写在嵌套函数中或 if/for等语句中
    return (
        <div>
            {/* 4、使用数据，修改数据：在方法中传入一个新值  */}
            <button onClick={() => setCount(count + 1)}>{count}</button>
        </div>
    )  
}

/*
    hooks 解决函数副作用。      个人理解：[副作用是指，函数体里的操作会影响函数外的值] (纯函数没有副作用)
    使用 useEffect 解决副作用
        1、引入 useEffect 函数  import {useEffect} from 'react'
        2、在函数组件中执行这个函数，传入一个回调，并定义副作用
        3、当我们通过行状态更新组件时，副作用也会不断执行
*/
/*
    依赖项控制副作用的执行时机 （依赖项是 useEffect 的第二个参数）
    1、默认状态（无依赖项）
        组件初始化的时候先执行一次，等到每次数据修改组件更新再次执行
    2、添加一个空数组依赖项
        组件初始化的时候执行一次，更新不会再执行了
    3、添加特定依赖项
        组件初始化的时候执行一次，特定依赖项发生变化会再次执行

    注意事项：在useEffect回调函数中用到的数据状态，就应该出现在依赖项数组中声明，否则可能会有bug
*/
function Mod() {
    const [name, setName] = useState('zhangsan') 
    const [count, setCount] = useState(0) 
    // 执行
    // useEffect(() => {
    //     // 定义副作用
    //     console.log('执行了1')
    //     document.title = name
    // })
    // useEffect也可以多次使用
    // useEffect(() => {
    //     console.log('执行了2')
    //     document.title = count
    // }, [])
    // useEffect(() => {
    //     console.log('执行了3')
    //     document.title = count
    // }, [count])
    useEffect(() => {                   // 此时执行副作用函数的时机： 初始化 + count/name被修改时都会执行
        console.log('执行了3')
        document.title = count
        console.log(name)            
    }, [count, name])          // 在useEffect回调函数中用到的数据状态，就应该出现在依赖项数组中声明，否则可能会有bug
    return (
        <div>
            <button onClick={() => setName('lisi')}>{name}</button>
            <button onClick={() => setCount(count + 1)}>{count}</button>
        </div>
    )
}

// 自定义 hooks
function useWindowScroll() {
    // 获取滚动距离y
    const [y, setY] = useState(0)
    // 滚动行为发生的时候，不断获取滚动值，然后交给y
    window.addEventListener('scroll', () => {
        const h = document.documentElement.scrollTop
        setY(h)
    })
    
    return [y]
}

// 自定义 hooks , 可以自动同步到本地的LocalStorage
function useLocalStorage(key, defultValue) {  // 可以传入初始值
    const [message, setMessage] = useState(defultValue)
    // 每次只要message变化，就会自动同步到本地loaclStorage
    // 就可以使用useEffect
    useEffect(() => {
        window.localStorage.setItem(key, message)
    }, [message, key])
    return [message, setMessage]
}

// useState 使用函数作为参数， 当需要计算传入的参数时，可以使用函数作为参数，计算需要使用的值
function Counter (props) {

    // const [count, setCount] = useState(0)
    const [count, setCount] = useState(() => {
        // 这里例子局体现初始值需要经过一定的计算， 表示无法直接确定,需要通过一定的操作才能获取的
        return props.count + 1
    })
    return (
        <button onClick={() => setCount(count + 1)}>{count}</button>
    )
}

// 清除副作用函数，比如清除定时器
function Timer () {

    // useEffect(() => {
    //     let timer = setInterval(() => {
    //         console.log('定时器执行了')
    //     }, 1000)

    //     return () => {           // 最后返回的回调函数中清除副作用函数
    //         // 清理动作
    //         clearInterval(timer)
    //     }
    // })
    return (
        <div>
            this is timer component
        </div>
    )
}

// useEffect 发送网络请求
/*
    类组件做异步请求： 在componentDidMount 中请求，相当于在初始化的时候dom渲染完成后执行一次

    useEffect: 在加 空数组依赖项的时候， 初始化执行一次 []
*/
function Ajax () {

    useEffect(() => {
        async function fetchData() {       // 使用async 函数
            /*
                fetch的使用
                params ={ method: 'get/post', header: '', .... }
                fetch(url, params).then(response => response.json()).then(data => console.log(data))
            */
            const res = await fetch('http://localhost:3000')
            console.log(res)
        }
        fetchData()
    }, [])
    return (
        <div>
            this is ajax component
        </div>
    )
}

/*
    useRef 函数的使用： 在hooks环境下获取dom的api
        导入 useRef import {useRef} from 'react'
        执行useRef 函数并传入null, 返回值作为一个对象，内部有一个current属性存放拿到的dom对象（普通元素/标签）或组件实例（类组件）
        通过 ref 绑定元素或组件
*/
function Ins () {
    const refH = useRef(null)
    const myRef = useRef(null)

    useEffect(() => {
        console.log(refH);
        console.log(refH.current);
        console.log(myRef.current);  // 组件内的属性和方法都可以调了
    }, [])
    // useEffect回调是在dom渲染完成之后调用的
    return (
        <div>
            this is ref component
            <h1 ref={refH}>标题</h1>
            {/* 只能在类组件上使用 ref , 函数组件没有 */}
            <M ref={myRef} />
        </div>
    )
}
class M extends React.Component {
    getName = () => {

    }
    render() {
        return (
            <div>类组件</div>
        )
    }
}

/*
    useContext 的使用： hooks里 使用createContext   跨组件传值
        先引入
        传递数据使用 Provider
        接收数据使用useContext

        正常跨组件传值：最好是包裹在最顶层组件，全局传递， 静态值可以再index.js中使用包裹（传递一次）， 动态参数可以在App.js中使用包裹
*/
const Context = createContext()
const {Provider} = Context
 
function ComA() {
    // 这里就使用 useContext 来接收传过来的hooks参数, 值是响应式的
    const count = useContext(Context)    // 这里的参数就是 createContext 创建的Context
    return (
        <div>this is ComA, 这是ComC传过来的数据{count}</div>
    )
}
function ComB() {
    const count = useContext(Context)   
    return (
        <div>
            this is ComB, 这是ComC传过来的数据{count}
            <ComA />
        </div>
    )
}

function ComC() {
    const [count, setCount] = useState(0)
    return (
        // 这里传递的是 useState 中的参数
        <Provider value={count}>
            <div>
                this is ComC
                <ComB />
                <button onClick={() => setCount(count + 1)}>+</button>
            </div>
        </Provider>
    )
}


export default function Test11_hooks() {
    // 使用自定义 hooks
    const [y] = useWindowScroll()

    const [message, setMessage] = useLocalStorage('mkey','本地数据')
    setTimeout(() => {
        setMessage('接收数据')
    }, 5000)

    const [flag, setFlag] = useState(true)

  return (
    <div style={{height: '10000px'}}>
        <Test />
        <Mod />
        {y}
        {message}

        <Counter count={10} />

        {/* 控制组件显示隐藏 */}
        {flag ? <Timer /> : null}
        <button onClick={() => setFlag(!flag)}>switch</button>

        <Ajax />

        <Ins />

        <ComC />
    </div>
  )
}
